### Класс Connection

Класс `irbis::Connection` -- "рабочая лошадка" библиотеки `PlusIrbis`. Он осуществляет связь с сервером и всю необходимую перепаковку данных из клиентского представления в сетевое.

Экземпляр клиента создается конструктором без параметров. Допускается создание экземпляра как на стеке, так и в куче.

```c++
irbis::Connection client; // на стеке
или
auto client = new irbis::Connection; // в куче
```

Экземпляр клиента не может быть перемещён или скопирован, т. к. соответствующие конструкторы и операторы запрещены.

При создании клиента можно указать (некоторые) настройки:

```c++
irbis::Connection client;
client.host = L"irbis.rsl.ru";
client.port = 5555;
client.username = "ninja";
client.password = "i_am_invisible";
```

Подавляющее большинство строк в библиотеке имеют тип `std::wstring`.

Поле        | Тип           | Назначение                  | Значение по умолчанию
------------|---------------|-----------------------------|----------------------
host        | std::wstring  | Адрес сервера               | L"127.0.0.1"
port        | short         | Порт                        | 6666
username    | std::wstring  | Имя (логин) пользователя    | L""
password    | std::wstring  | Пароль пользователя         | L""
database    | std::wstring  | Имя базы данных             | L"IBIS"
workstation | std::wstring  | Тип АРМа (см. таблицу ниже) | L"C"

Типы АРМов

Обозначение | Тип
------------|-----
L"R"        | Читатель
L"C"        | Каталогизатор
L"M"        | Комплектатор
L"B"        | Книговыдача
L"K"        | Книгообеспеченность
L"A"        | Администратор

Обратите внимание, что адрес сервера задается строкой, так что может принимать как значения вроде `192.168.1.1`, так и `irbis.yourlib.com`.

Если какой-либо из вышеперечисленных параметров не задан явно, используется значение по умолчанию.

#### Подключение к серверу и отключение от него

Только что созданный клиент еще не подключен к серверу. Подключаться необходимо явно с помощью метода `connect`, при этом можно указать параметры подключения:

```c++
irbis::Connection client;
client.host = L"myhost.com";
client.username = L"ninja";
client.password = L"i_am_invisible";
if (!client.connect()) {
    std::wcout << L"Не удалось подключиться!\n";
    std::wcout << irbis::describeError(client.lastError) << L"\n";
    std::exit(1);
}
```

Отключаться от сервера можно двумя способами: во-первых, с помощью метода `disconnect`:

```c++
client.disconnect();
```

во-вторых, клиент автоматически отключается от сервера при уничтожении экземпляра класса (при вызове `delete` или по выходу из области видимости):

```c++
delete client; // Происходит неявный вызов disconnect()
```

При подключении клиент получает с сервера INI-файл с настройками, которые могут понадобиться в процессе работы:

```c++
client.connect();
// Получаем имя MNU-файла, хранящего перечень форматов
const auto formatMenuName = client.iniFile.getValue(L"Main", L"FmtMnu", L"FMT31.MNU");
```

Полученный с сервера INI-файл хранится в поле `iniFile`.

Повторная попытка подключения с помощью того же экземпляра `Connection` игнорируется. При необходимости можно создать другой экземпляр и подключиться с его помощью (если позволяют клиентские лицензии). Аналогично игнорируются повторные попытки отключения от сервера.

Проверить статус "клиент подключен или нет" можно с помощью метода `connected()`:

```c++
if (!client.connected()) {
    // В настоящее время мы не подключены к серверу
}
```

Вместо индивидуального задания каждого из полей `host`, `port`, `username`, `password` и `database`, можно использовать метод `parseConnectionString`:

```c++
client.parseConnectionString(L"host=192.168.1.4;port=5555;"
         + L"username=itsme;password=secret;");
client.connect();
``` 

#### Многопоточность

Клиент написан в наивном однопоточном стиле, поэтому не поддерживает одновременный вызов методов из разных потоков.

Для одновременной отсылки на сервер нескольких команд необходимо создать соответствующее количество экземпляров подключений (если подобное позволяет лицензия сервера).

#### Подтверждение подключения

`PlusIrbis` самостоятельно не посылает на сервер подтверждений того, что клиент все еще подключен. Этим должно заниматься приложение, например, по таймеру. 

Подтверждение посылается серверу методом `noOp`:
 
```c++
if (!client.noOp()) {
    std::wcout << L"Сбой подтверждения!\n";
}        
```

#### Чтение записей с сервера

```c++
auto mfn = 123;
auto record = client.readRecord(mfn);        
```

Можно прочитать несколько записей сразу:

```c++
auto std::vector<int> mfns { 12, 34, 56 };
auto records = client.readRecords(mfns);
```

Можно прочитать определенную версию записи

```c++
auto mfn = 123;
auto version = 3;
auto record = client.readRecord(mfn, version);
```

#### Сохранение записи на сервере

```c++
// Любым образом создаём или получаем с сервера запись.
auto record = client.readRecord(123);

// Производим какие-то манипуляции над записью
record.addField(999, L"123");

// Отсылаем запись на сервер
newMaxMfn = client.writeRecord(record);
std::wcout << L"Новое значение MaxMFN: " << newMaxMfn << L"\n";
```

Сохранение нескольких записей (возможно, из разных баз данных):

```c++
std::vector<MarcRecord> records;
// как-нибудь наполняем контейнер записями.
...
if (!client.writeRecords(records))) {
    std::wcout << L"Сбой записи!";
}
```

#### Удаление записи на сервере

```c++
auto mfn = 123;
client.deleteRecord(mfn);
```

Восстановление записи:

```c++
auto mfn = 123;
auto record = client.undeleteRecord(mfn);
```

#### Поиск записей

```c++
auto found = client.search(L"\"A=ПУШКИН$\"");
std::wcout << L"Найдено записей: " << found.size() << L"\n";
```

Обратите внимание, что поисковый запрос заключен в дополнительные кавычки. Эти кавычки явлются элементом синтаксиса поисковых запросов ИРБИС64, и лучше их не опускать.

Вышеприведённый запрос вернёт не более 32 тыс. найденных записей. Сервер ИРБИС64 за одно обращение к нему может выдать не более 32 тыс. записей. Чтобы получить все записи, используйте метод `searchAll` (см. ниже), он выполнит столько обращений к серверу, сколько нужно.

Поиск с одновременной загрузкой записей:

```c++
auto records = client.searchRead(L"\"A=ПУШКИН$\"", 50);
std::wcout << L"Найдено записей: " << records.size() << L"\n";
```

Поиск и загрузка единственной записи:

```c++
auto record = client.searchSingleRecord(L"\"I=65.304.13-772296\"");
if (record.empty()) {
    std::wcout << L"Не нашли!\n";
}
```

Количество записей, соответствующих поисковому выражению:

```c++
auto expression = L"\"A=ПУШКИН$\"";
auto count = client.searchCount(expression);
```

Расширенный поиск: можно задать не только количество возвращаемых записей, но и расформатировать их.

```c++
irbis::SearchParameters parameters;
parameters.expression = L"\"A=ПУШКИН$\"";
parameters.format = L"@brief";
parameters.numberOfRecords = 5;
auto found = client.search(parameters);
if (found.empty()) {
    std::wcout << L"Не нашли!\n";
} else {
    // в found находится массив FoundLine
    auto first = found.front();
    std::wcout << L"MFN: " << first.mfn << L"DESCRIPTION: " << first.description << L"\n";
}
```

Поиск всех записей (даже если их окажется больше 32 тыс.):

```c++
auto found = client.searchAll(L"\"A=ПУШКИН$\"");
std::wcout << L"Найдено записей: " << found.size() << L"\n";
```

Подобные запросы следует использовать с осторожностью, т. к. они, во-первых, создают повышенную нагрузку на сервер, и во-вторых, потребляют очень много памяти на клиенте. Некоторые запросы (например, "I=$") могут вернуть все записи в базе данных, а их там может быть десятки миллионов.

#### Форматирование записей

```c++
auto mfn = 123;
auto format = L"@brief";
auto text = client.formatRecord(format, mfn);
std::wcout << L"Результат форматирования: " + text + L"\n";
```

При необходимости можно использовать в формате все символы UNICODE:

```c++
auto mfn = 123;
auto format = L"'Ἀριστοτέλης: ', v200^a";
auto text = client.formatRecord(format, mfn);
std::wcout << L"Результат форматирования: " + text + L"\n";
```

Форматирование нескольких записей:

```c++
std::vector<int> mfns { 12, 34, 56 };
auto format = L"@brief";
auto lines = client.formatRecords(format, mfns);
std::wcout << L"Результаты:\n";
for (const auto &line : lines ) {
    std::wcout << line << L"\n";
}
```

#### Печать таблиц

```c++
irbis::TableDefinition table;
table.database = L"IBIS";
table.table = L"@tabf1w";
table.searchQuery = L"\"T=A$\"";
auto text = client.printTable(table);
```

#### Работа с контекстом

Функция            | Назначение
-------------------|-----------
listFiles          | Получение списка файлов на сервере
readIniFile        | Получение INI-файла с сервера
readMenuFile       | Получение MNU-файла с сервера
readSearchScenario | Загрузка сценариев поиска с сервера
readTextFile       | Получение текстового файла с сервера
readTextLines      | Получение текстового файла в виде массива строк
readTreeFile       | Получение TRE-файла с сервера
updateIniFile      | Обновление строк серверного INI-файла
writeTextFile      | Сохранение текстового файла на сервере

#### Работа с мастер-файлом

Функция        | Назначение
---------------|-----------
readRawRecord  | Чтение указанной записи в "сыром" виде
writeRawRecord | Сохранение на сервере "сырой" записи

#### Работа со словарем

Функция           | Назначение
------------------|-----------
getRecordPostings | Массив постингов для указанных MFN и префикса
listTerms         | Получение списка терминов с указанным префиксом
readPostings      | Чтение постингов поискового словаря
readTerms         | Чтение терминов поискового словаря
readTermsEx       | Расширенное чтение терминов

#### Информационные функции

Функция            | Назначение
-------------------|-----------
getDatabaseInfo    | Получение информации о базе данных
getMaxMfn          | Получение максимального MFN для указанной базы данных
getServerVersion   | Получение версии сервера
listDatabases      | Получение списка баз данных с сервера
toConnectionString | Получение строки подключения

#### Администраторские функции

Нижеперечисленные записи доступны лишь из АРМ "Администратор", поэтому подключаться к серверу необходимо так:

```c++
irbis::Connection client;
client.username = L"librarian";
client.password = L"secret";
client.workstation = L"A"; // обратите внимание!
if (!client.connect()) {
    std::wcout << L"Не удалось подключиться!\n";
    exit(1);
}
```

Функция           | Назначение
------------------|-----------
actualizeDatabase | Актуализация базы данных (всех неактуализированных записей)
actualizeRecord   | Актуализация записи
createDatabase    | Создание базы данных
createDictionary  | Создание словаря
deleteDatabase    | Удаление базы данных
deleteFile        | Удаление файла на сервере 
getServerStat     | Получение статистики с сервера
getUserList       | Получение списка пользователей с сервера
listProcesses     | Получение списка серверных процессов
reloadDictionary  | Пересоздание словаря
reloadMasterFile  | Пересоздание мастер-файла
restartServer     | Перезапуск сервера
truncateDatabase  | Опустошение базы данных
unlockDatabase    | Разблокирование базы данных
unlockRecords     | Разблокирование записей
updateUserList    | Обновление списка пользователей на сервере

#### Этапы выполнения запроса

Любой запрос к серверу проходит через следующие этапы:

1. Формирование пакета запроса.
2. Разрешение имени сервера.
3. Установка подключения к серверу.
4. Передача пакета запроса на сервер.
5. Ожидание ответа от сервера.
6. Получение ответного пакета от сервера.
7. Отключение от сервера.
8. Разбор заголовка ответа.
9. Разбор тела ответа (если есть).


[Предыдущая глава](chapter1.md) | [Следующая глава](chapter2.md)
